﻿#include "log.h"

#include "spdlog/async.h" //support for async logging
#include "spdlog/async_logger.h"
#include "spdlog/details/thread_pool-inl.h"
#include "spdlog/details/thread_pool.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
#include <iostream>
#include <memory>
#include <spdlog/async.h>
#include <spdlog/formatter.h>

namespace cppc { namespace log {

void setLoggerLevel(std::shared_ptr<spdlog::logger> logger, log::LogLevel level) {
    switch (level) {
    case log::LOG_DEBUG: logger->set_level(spdlog::level::debug); break;
    case log::LOG_INFO: logger->set_level(spdlog::level::info); break;
    case log::LOG_WARN: logger->set_level(spdlog::level::warn); break;
    case log::LOG_ERROR: logger->set_level(spdlog::level::err); break;
    case log::LOG_FATAL: logger->set_level(spdlog::level::critical); break;
    case log::LOG_OFF: logger->set_level(spdlog::level::off); break;
    case log::LOG_TRACE:
    case log::LOG_ALL:
    default: logger->set_level(spdlog::level::trace); break;
    }
}

static std::atomic_bool s_threadHasInit{false};
/// 初始化异步日志的线程池
void initLogThreadPool() {
    if (!s_threadHasInit) {
        spdlog::init_thread_pool(10240, 1);
        spdlog::flush_every(std::chrono::seconds(1));
        s_threadHasInit = true;
    }
}

bool initRootRollingFileLog(log::LogLevel level, const std::string &filePath, size_t maxFileSize,
                            unsigned int maxFiles, const std::string &patternLayout, bool append,
                            bool appendToConsole) {
    bool initFlag = initRollingFileLog(level, ROOT_LOGGER_NAME, filePath, maxFileSize, maxFiles,
                                       patternLayout, append, appendToConsole);
    if (initFlag) {
        spdlog::set_default_logger(getLogger(ROOT_LOGGER_NAME));
    }
    return initFlag;
}

bool initRollingFileLog(log::LogLevel level, const std::string &loggerName,
                        const std::string &filePath, size_t maxFileSize, unsigned int maxFiles,
                        const std::string &patternLayout, bool append, bool appendToConsole) {
    initLogThreadPool();
    bool t_initState = false;
    try {
        // sink容器
        std::vector<spdlog::sink_ptr> vecSink;

        if (appendToConsole) { // 控制台
            auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
            vecSink.push_back(console_sink);
        }
        { // 文件
            auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
                filePath, maxFileSize, maxFiles, !append);
            vecSink.push_back(file_sink);
        }

        auto tp = spdlog::thread_pool();

        // 异步文件日志
        // 设置logger使用多个sink
        std::shared_ptr<spdlog::logger> t_logger = getLogger(loggerName);
        if (t_logger) {
            spdlog::drop(loggerName);
        }

        auto logger = std::make_shared<spdlog::async_logger>(
            loggerName, begin(vecSink), end(vecSink), tp, spdlog::async_overflow_policy::block);
        logger->set_pattern(patternLayout, spdlog::pattern_time_type::local);
        // 遇到指定级别，立即flush到文件
        logger->flush_on(spdlog::level::debug);
        setLoggerLevel(logger, level);

        // 注册到spdlog
        spdlog::register_logger(logger);
        t_initState = true;
    } catch (const spdlog::spdlog_ex &ex) { t_initState = false; }
    return t_initState;
}

std::shared_ptr<spdlog::logger> getLogger(const std::string &loggerName) {
    auto logger = loggerName.empty() ? spdlog::default_logger() : spdlog::get(loggerName);
    return logger;
}

void closeLog(const std::string &loggerName) {
    spdlog::drop(loggerName);
}
void closeAllLog() {
    getLogger(ROOT_LOGGER_NAME)->flush();
    spdlog::shutdown(); // 关闭日志器和线程池
    s_threadHasInit = false;
}
}} // namespace cppc::log
